<?php
/**
 * Class Responsible for create the crud system
 * for classes childrens of the Nucleo_Model_Object
 *
 *  @version 1.0
 *  @package Nucleo
 *  @subpackage Nucleo.Model
 *  @copyright 2009-2010 Renan Abreu
 *
 *  @author Renan Henrique Abreu <renanhabreu@gmail.com>
 */
abstract class Nucleo_Model_Crud {
    /**
     *Numero de resultados por pagina na lista de resultados
     * @var int
     */
    static public $pages = 10;
    
    /**
     * O Tipo de envio do formulario HTML
     * @var string
     */
    static public $method = 'POST';
    
    /**
     *Nome do template para o CRUD
     * @var string
     */
    static public $template = 'crud';

    static public $pages_select = true;

    /**
     * Vetor contendo as informações para a url do CRUD
     * @var array
     */
    static private $url = array();
    
    
    /**
     * Cria um novo crud para a classe $class. Este metodo configura toda a tela
     * para o sistema de crud
     *
     * @param Object $class Classe do tipo Nucleo_Model_Object
     * @param array $fkLabel Array Contendo os rotulos e campos para FKs
     * @param array $urlBase Base url do crud
     */
    static public function newCrud($class,$fkLabel = null,$urlBase = null) {
        //CRUD HTML
        $CONTAINER = NULL;
        
        //configurando a URL do crud
        if(!isset($urlBase))
            $urlBase = Array('m'=>DEFAULT_MODULE,'c'=>DEFAULT_CONTROLLER,'a'=>DEFAULT_ACTION);
        self::$url[$class] = Nucleo_Controller_Controller::url($urlBase);
        
        if(class_exists($class)) {
            eval('$ObjectToCrud = '.$class.'::create();');
            
            //Capturando lista html dos dados existentes
            $ACTI = self::getAction($ObjectToCrud);
            $LIST = self::getScreenList($ObjectToCrud,$fkLabel,$class);
            $FORM = self::getScreenForm($ObjectToCrud,$fkLabel,$class);
            
            $template = LOCAL_FRAMEWORK.'Nucleo/Model/Crud/'.self::$template.'.tpl.php';
            if(file_exists($template)) {
                $CONTAINER = file_get_contents($template);
                $CONTAINER = str_replace('{SIFRAPHP_CRUD_TITLE}',$class, $CONTAINER);
                $CONTAINER = str_replace('{SIFRAPHP_CRUD_FORM}', $FORM, $CONTAINER);
                $CONTAINER = str_replace('{SIFRAPHP_CRUD_LIST}',$LIST, $CONTAINER);
            }else {
                throw new Nucleo_Exception_Template('027');
            }
        }else {
            throw new Nucleo_Exception_Sifra('028',true);
        }
        
        return $CONTAINER;
    }
    
    /**
     * Cria a lista de objetos que estao gravados no banco de dados
     * @param Nucleo_Model_Object $ObjectToCrud
     * @param array $fkLabel Array contentdo os rotulos para Fks
     * @param string $class Nome da classe para criacao do CRUD
     */
    static private function getScreenList(Nucleo_Model_Object $ObjectToCrud,$fkLabel,$class) {
        $fieldsToList = null;
        
        //get para capturar a paginacao
        $GET = Nucleo_Registry_Registry::get('GET');
        
        $paginator = Nucleo_Model_Paginator::getValues($ObjectToCrud,self::$pages,$GET['crud_start']);

        $html['pag']['pag']['pag']['start'] = $paginator['previous'];
        $html['pag']['pag']['pag']['end']   = $paginator['next'];
        $html['pag']['pag']['pag']['max']   = ceil($paginator['max']/self::$pages);
        $pag_limit                          = $paginator['limit'];
        //FIM de regra de paginacao
        
        //Lista Objetos
        if( isset($ObjectToCrud)) {
            $ObjectsList = $ObjectToCrud->findAll('LIMIT '.$pag_limit.','.self::$pages);
            $colsName  = self::getFieldsLabel($ObjectToCrud->getObjectCols(),$fkLabel);
            $html['fields']['titles']['names'][0] = $colsName;
            
            //FIM de rotulos alternativos
            
            foreach($ObjectsList as $ObjectInDB) {
                //Lista de ids dos objetos
                $html['pk']['datas']['id'][] = $ObjectInDB->getPkValue();
                
                //mapeando os valores de objetos para array
                foreach($html['fields']['titles']['names'][0] as $name) {
                    $getField = 'get'.$name;
                    $data = $ObjectInDB->$getField();
                    
                    //Caso seja uma chave estrangeira
                    $keyFk    = array_search($name, $html['fields']['titles']['names'][0]);
                    if(isset($fkLabel[$keyFk])) {
                        $getFieldFk = $fkLabel[$keyFk];
                        $getField = 'get'.$getFieldFk['fieldFk'];
                        $children = $ObjectInDB->getChildrens();
                        $objChildren = $children[$ObjectInDB->getFkTable($keyFk)];
                        $data = $objChildren->$getField();
                    }
                    
                    $html['fields']['datas'][$ObjectInDB->getPkValue()][] = $data;
                }
            }
            // FIM DA LISTAGEM DE OBJETOS
            //gerando HTML com os dados do array
            $fieldsToList = self::getHtmlList($html,$class);
        }else {
            throw new Nucleo_Exception_Sifra('029');
        }
        
        return $fieldsToList;
        
    }
    
    /**
     *Cria o HTML bseado no array.
     * Css para tabela de resultados:
     *      .sifraphp-crud-result = Tabela de resultados
     *      .sifraphp-crud-result-title = Linha dos titulos da tabela resultado
     *
     * @param array $datas Array[fields][titles/datas][name/name_field][x]
     * @param string $class Nome da classe representada pelo CRUD
     */
    static private function getHtmlList(array $fieldsToList,$class) {
        $GET = Nucleo_Registry_Registry::get('GET');

        $crud_start = '';
        if(isset($GET['crud_start']))
            $crud_start = '/crud_start/'.$GET['crud_start'];
        
        //listando titulos da tabela
        $HTML['titles'] = '<tr class="sifraphp-crud-result-title">';
        $HTML['fields'] = '';
        
        $FIELDS = '';
        foreach($fieldsToList['fields']['titles']['names'][0] as $fieldsTitle) {
            $HTML['titles'] .= '<td>'.$fieldsTitle.'</td>';
        }
        
        $HTML['titles'] .= '</tr>';
        // FIM DE TITULOS
        
        //Listando Dados
        if(isset($fieldsToList['pk']['datas']['id'])) {
            foreach($fieldsToList['pk']['datas']['id'] as $fieldPkValue) {
                
                //Criando TDs para tabela resultados
                foreach ($fieldsToList['fields']['datas'][$fieldPkValue] as $value) {
                    $FIELDS .= '<td>'.$value.'</td>';
                }
                $url_action_upd = self::$url[$class].'crud/update/id/'.$fieldPkValue;
                $url_action_del = self::$url[$class].'crud/delete/id/'.$fieldPkValue;
                
                $LINK_UPD_DEL = '<td><a href="'.$url_action_del.$crud_start.'">Delete</a></td>'
                        . '<td><a href="'.$url_action_upd.$crud_start.'">Update</a></td>';
                
                $colNum = ($colNum == 1)? 2:1;
                $HTML['fields'] .= '<tr class="sifraphp-crud-result-'.$colNum.'">'.$FIELDS.$LINK_UPD_DEL.'</tr>';
                $FIELDS = '';
            }
            $colspan = sizeof($fieldsToList['fields']['datas'][$fieldPkValue]) + 2;
            $crud_start = self::$url[$class].'crud_start/'.$fieldsToList['pag']['pag']['pag']['start'];
            $crud_end = self::$url[$class].'crud_start/'.$fieldsToList['pag']['pag']['pag']['end'];

            $select = '';
            if(self::$pages_select && $fieldsToList['pag']['pag']['pag']['max'] > 1 ){
                $script = 'onChange="redirect(\''.self::$url[$class].'crud_start/\',this)" ';
                $num_pags = $fieldsToList['pag']['pag']['pag']['max'];
                $select_page = '<option value="0" selected="selected">Pages</option>';
                for($i = 0; $i < $num_pags; $i++){
                    $select_page .= '<option value="'.$i.'">'.$i.'</option>';
                }

                $select = '<select name="select_page" '.$script.' >'.$select_page.'</select>';
            }

            if($fieldsToList['pag']['pag']['pag']['max'] > 1){
                $HTML['fields'] .='<tr>'
                                 .'     <td colspan="'.$colspan.'">'
                                 .'         <a href="'.$crud_start.'">&lt;&lt</a>'
                                 .'         <a href="'.$crud_end.'">&gt;&gt</a>'
                                 .$select
                                 .'     </td>'
                                 .'</tr>';
            }
        }
        return '<table class="sifraphp-crud-result">'.implode('',$HTML).'</table>';
    }
    
    /**
     *Define a acao a ser tomada pelo CRUD.
     * Verifica se acao será de insercao/atualizacao ou exclusao
     * Do objeto
     * @param Nucleo_Model_Object $ObjectToCrud
     */
    static private function getAction(Nucleo_Model_Object $ObjectToCrud) {
        $GET = Nucleo_Registry_Registry::get('GET');
        $POST = Nucleo_Registry_Registry::get('POST');
        
        if(isset($GET['crud']) &&  strtolower($GET['crud']) == 'save') {
            //capturar dados
            //verificar se os dados estao em branco
            //criar um novo objeto
            //salvar novo objeto\s
            $POST_ID = $POST[$ObjectToCrud->getPkName()] ;
            
            if(trim($POST_ID) != '' ) {
                $ObjectToCrud->findById($POST_ID);
            }
            
            foreach($POST as $field=>$value) {
                if($field != 'sifraphp-crud-submit' && !empty($value)) {
                    $set = 'set'.$field;
                    $ObjectToCrud->$set($value);
                }
            }
            
            try {
                $ObjectToCrud->save();
            }catch (PDOException $e) {
                echo 'Erro : '.$e->getMessage();
                exit();
            }
        }elseif(isset($GET['crud']) && strtolower($GET['crud']) == 'delete') {
            
            $where = 'WHERE '.$ObjectToCrud->getPkName().'= ? LIMIT 0,1';
            $delete = $ObjectToCrud->findAll($where,Array($GET['id']));
            
            if(sizeof($delete) > 0) {
                try {
                    $objDeleted = $delete[0];
                    if(!$objDeleted->delete())
                        throw new Nucleo_Exception_Sifra('030');
                }catch (PDOException $e) {
                    echo 'Erro :'.$e->getMessage();
                    exit();
                }
            }
        }
    }
    
    /**
     *Cria o formulario do crud
     * @param Nucleo_Model_Object $ObjectToCrud
     * @param array $fkLabel
     * @param string $class
     * @return string HTML
     */
    static private function getScreenForm(Nucleo_Model_Object $ObjectToCrud,$fkLabel,$class) {
        //Verifica se algum dado devera ser inserido no formulario
        $GET = Nucleo_Registry_Registry::get('GET');
        $UPDATE['action'] = $GET['crud'];
        $UPDATE['object'] = $GET['id'];
        
        //Formulario a ser impresso
        $FORM = null;
        
        //Busca dados para atualizacao
        $ObjUpdate = null;
        if(isset($UPDATE['action']) && isset($UPDATE['object'])) {
            $ObjUpdate = $ObjectToCrud->findById($UPDATE['object']);
        }
        
        //Lista campos a a serem impressos
        $COLS = $ObjectToCrud->getObjectCols();
        $labels = self::getFieldsLabel($COLS,$fkLabel);
        
        foreach($COLS as $value) {
            $__update = '';
            $__update_select = '';
            $__value_to_select = null;
            $fkTable = $ObjectToCrud->getFkTable($value);
            
            if($ObjUpdate) {
                $getField = 'get'.$value;
                $value_up = $ObjectToCrud->$getField();
                $__update = ' value="'.$value_up.'" ';
                $__value_to_select = $value_up;
            }
            
            if(!isset($fkTable)) {
                $input = '<input type="text" name="'.$value.'" '.$__update.' />';
            }else {
                eval('$objsOpts = '.ucfirst(strtolower($fkTable)).'::create();');
                $objTable = $objsOpts->findAll();
                $input = '<select name="'.$value.'">';
                
                if(sizeof($objTable)) {
                    foreach($objTable as $Object) {
                        $get = 'get'.$Object->getPkName();
                        $getFieldValue = $get;
                        $value_field = $Object->$get();
                        if(isset($fkLabel[$value])) {
                            $fieldFk = $fkLabel[$value];
                            $getFieldValue = 'get'.$fieldFk['fieldFk'];
                        }
                        
                        if(isset($__value_to_select) && $__value_to_select == $value_field) {
                            $__update_select = ' selected="selected" ';
                        }
                        $input .= '<option value="'.$value_field.'" '.$__update_select.'>'
                                .  $Object->$getFieldValue()
                                .'</option>';
                        if(!empty ($__update_select))
                            $__update_select = '';
                    }
                    
                }
                $input .= '</select>';
            }
            
            $FORM   .= '<tr class="sifraphp-crud-form-field">';
            $FORM   .= '<td class="sifraphp-crud-form-field-label">'.$labels[$value].'</td>';
            $FORM   .= '<td class="sifraphp-crud-form-field-input">'.$input.'</td>';
            $FORM   .= '</tr>';
        }
        
        $SUBMIT ='<tr class="sifraphp-crud-form-submit">'
                .'      <td colspan="2" ><input type="submit" value="Save" name="sifraphp-crud-submit"/></td>'
                .'</tr>';
        
        $ACTION['start'] = '<form class="sifraphp-crud-form" action="'.self::$url[$class].'crud/save/'
                .'" method="'.self::$method
                .'" name="'.$class.'" >';
        $ACTION['end']   = '</form>';
        
        return $ACTION['start'].'<table>'.$FORM.$SUBMIT.'</table>'.$ACTION['end'];
    }
    
    /**
     *Formata os rotulos da tela de listagem e formulario CRUD
     *
     * @param array $array_labels
     * @param array $array_alternative
     * @return string HTML
     */
    static private function getFieldsLabel($array_labels,$array_alternative) {
        //Criando Rotulos alternaivos para FKs
        if(isset($array_alternative)) {
            $colsNameAlt = Array();
            foreach ($array_labels as $col) {
                $description = $array_alternative[$col];
                if(isset($description)) {
                    $colsNameAlt[$col] = $description['label'];
                }else {
                    $colsNameAlt[$col] = $col;
                }
            }
        }else {
            foreach($array_labels as $field) {
                $colsNameAlt[$field] = $field;
            }
        }
        
        return $colsNameAlt;
    }
    
    
}
